
using OmegaBot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using OmegaBot.Extensions;


namespace Spire
{
    public class SpireCombat : CombatClass
    {
		#region Fields
		public SpellKeyDefinition berserking;
        public SpellKeyDefinition bloodFury;
        public SpellKeyDefinition bloodlust;
        public SpellKeyDefinition callAncestors;
        public SpellKeyDefinition callElements;
        public SpellKeyDefinition callSpirits;
        public SpellKeyDefinition chainLightning;
        public SpireSetup Config;
        public SpellKeyDefinition cureToxins;
        public SpellKeyDefinition earthlivingWeapon;
        public SpellKeyDefinition earthShock;
        public SpellKeyDefinition feralSpirit;
        public SpellKeyDefinition flameShock;
        public SpellKeyDefinition flametongueWeapon;
        public SpellKeyDefinition frostbrandWeapon;
        public SpellKeyDefinition frostShock;
        public SpellKeyDefinition ghostWolf;
        public SpellKeyDefinition groundingTotem;
        public SpellKeyDefinition healingWave;
        public List<PolyValues> HealthTable =  
		new List<PolyValues>{new PolyValues(0.16638054, 0.28640034, 0.015159439, 0.05),
            new PolyValues(0.34749224, 0.33082224, 0.017150582, 0.075),
            new PolyValues(0.39689877, 0.28931695, 0.034447956, 0.1) };
        public SpellKeyDefinition heroism;
        public IniManager Ini;
        private bool isMounted = false;
        public Location LastTotemLocation = new Location(0f, 0f, 0f);
        public SpellKeyDefinition lavaBurst;
        public SpellKeyDefinition lavaLash;
        public readonly int LEFT = 0x25;
        public SpellKeyDefinition lesserHealingWave;
        public SpellKeyDefinition lightningBolt;
        public SpellKeyDefinition lightningShield;
        public SpellKeyDefinition mount1;
        public SpellKeyDefinition mount2;
        private bool moving=false;
        public readonly int RIGHT = 0x27;
        public SpellKeyDefinition rockbiterWeapon;
        public SpellKeyDefinition shamanisticRage;
        public SpellKeyDefinition stoneclawTotem;
        public SpellKeyDefinition stoneskinTotem;
        public SpellKeyDefinition stormStrike;
        public SpellKeyDefinition thunderstorm;
        public SpellKeyDefinition Totem;
        public SpellKeyDefinition totemicRecall;
        public bool TotemsNeedRecall = false;
        private bool turningLeft = false;
        private bool turningRight = false;
        public readonly int UP = 0x26;
        public SpellKeyDefinition waterShield;
        private DateTime weaponTimer = DateTime.MinValue;
        public SpellKeyDefinition windfuryTotem;
        public SpellKeyDefinition windfuryWeapon;
        public SpellKeyDefinition windshear;
		public readonly double MELEE_RANGE = 5.0;  
		public readonly double AGGRO_RANGE = 15.0;  
		public readonly double SHOCK_RANGE = 27.5;  
		public readonly double PULL_RANGE = 32.5;
		public readonly double MIN_MANA = .075; // This better be enough for at least 1 healings of urself!

		public GameUnit Target;
       	public override bool CanDrink { get { return true; } }
        public bool ChainAdds { get { return Config.ChainAdds.Checked; } }
        public bool ChaseRunners { get { return Config.ChaseRunners.Checked; } }
        public override string CombatClassName { get { return "Spire"; } }
        public double GhostDistance { get { return (double) Config.GhostDistance.Value; } }
        public double HealingWaveHealth { get { return (((double) Config.HealingWaveHealth.Value) / 100.0); } }
        public double LesserHealingWaveHealth { get { return (((double) Config.LesserHealingWaveHealth.Value) / 100.0); } }
        public bool LightningBoltAlways { get { return (PullWithLightningBolt && Config.LightningBoltOption.Text == "Always"); } }
        public bool LightningBoltOnce { get { return (PullWithLightningBolt && Config.LightningBoltOption.Text == "Once"); } }
        public bool LightningBoltUntilMeleeRange { get { return (PullWithLightningBolt && Config.LightningBoltOption.Text == "> 8 yards"); } }
        public SpellKeyDefinition MainHandEnchant { get { return GetWeaponEnchant(Config.MainHandEnchant.Text); } }
        public SpellKeyDefinition MainShock { get { return ((Config.MainShock.Text == "Frost") ? frostShock : ((Config.MainShock.Text == "Earth") ? earthShock : flameShock)); } }
        public double MeleeFinisherHealth { get { return (((double) Config.MeleeFinisherHealth.Value) / 100.0); } }
        public double MountDistance { get { return (double) Config.MountDistance.Value; } }
        public SpellKeyDefinition OffHandEnchant { get { return GetWeaponEnchant(Config.OffHandEnchant.Text); } }
        public bool PullWithLightningBolt { get { return (Config.PullOption.Text == "LBolt"); } }
        public bool PullWithShock { get { return (Config.PullOption.Text == "Shock"); } }
        public double ShamanisticRageMana { get { return (((double) Config.ShamanisticRageMana.Value) / 100.0); } }
        public double ShieldTwistingMana { get { return (((double) Config.ShieldTwistingMana.Value) / 100.0); } }
        public bool StoneclawAdds { get { return Config.StoneclawAdds.Checked; } }
        public string TotemID { get { return Config.TotemID.Text.Trim(); } }
        public string TotemName { get { return Config.TotemName.Text.Trim(); } }
        public bool UseCallOfAncestors { get { return Config.UseCallOfAncestors.Checked; } }
        public bool UseCallOfElements { get { return Config.UseCallOfElements.Checked; } }
        public bool UseCallOfSpirits { get { return Config.UseCallOfSpirits.Checked; } }
        public bool UseFlameShockDebuff { get { return Config.UseFlameShockDebuff.Checked; } }
        public bool UseFrostShockDebuff { get { return Config.UseFrostShockDebuff.Checked; } }
        public bool UseGhostWolf { get { return Config.UseGhostWolf.Checked; } }
        public bool UseMount { get { return Config.UseMount.Checked; } }
        public bool UseTotems { get { return Config.UseTotems.Checked; } }
	#endregion

		#region Setup

		public override void CreateSetup()
		{
			Ini = new IniManager("Spire.ini");
			Ini.SetComment("Spire.ini\r\n\r\nThe ultimate shaman custom class!");
			Ini.Set("combat", "PullOption", "");
			Ini.Set("combat", "LightningBoltOption", "");
			Ini.Set("combat", "UseFlameShock", "");
			Ini.Set("combat", "MeleeFinisherHealth", "");
			Ini.Set("combat", "StoneclawAdds", "");
			Ini.Set("combat", "ChainAdds", "");
			Ini.Set("buffs", "ShieldTwistingMana", "");
			Ini.Set("buffs", "ShamanisticRage", "");
			Ini.Set("buffs", "MainHandEnchant", "");
			Ini.Set("buffs", "OffHandEnchant", "");
			Ini.Set("buffs", "UseTotems", "");
			Ini.Set("buffs", "UseCallOfAncestors", "");
			Ini.Set("buffs", "UseCallOfSpirits", "");
			Ini.Set("buffs", "UseCallOfElements", "");
			Ini.Set("buffs", "TotemName", "");
			Ini.Set("buffs", "TotemID", "");
			Ini.Set("healing", "HealingWaveHealth", "");
			Ini.Set("healing", "LesserHealingWaveHealth", "");
			Ini.Set("movement", "UseGhostWolf", "");
			Ini.Set("movement", "ChaseRunners", "");
			Ini.Set("movement", "GhostDistance", "");
			Ini.Set("movement", "UseMount", "");
			Ini.Set("movement", "MountDistance", "");
			Ini.Load();
			Ini.Save();
			Config = new SpireSetup(this);
		}

		protected override ClassSetupPanel CreateSetupPanel()
		{
			return Config;
		}

		protected override void SetupKeys()
		{
			Log.WriteLine("Spire is setting up spell keys", new object[0]);
			flameShock = SpellKeys.CreateFromHostileSpell("flameshock", 0x1f72);
			earthShock = SpellKeys.CreateFromHostileSpell("earthshock", 0x1f6a);
			frostShock = SpellKeys.CreateFromHostileSpell("frostshock", 0x1f78);
			lavaBurst = SpellKeys.CreateFromHostileSpell("lavaburst", 0xc931);
			chainLightning = SpellKeys.CreateFromHostileSpell("chainlightning", 0x1a5);
			lightningBolt = SpellKeys.CreateFromHostileSpell("lightningbolt", 0x193);
			lesserHealingWave = SpellKeys.CreateFromSpell("lesserhealingwave", 0x1f44);
			healingWave = SpellKeys.CreateFromSpell("healingwave", 0x14b);
			cureToxins = SpellKeys.CreateFromSpell("curetoxins", 0x20e);
			flametongueWeapon = SpellKeys.CreateFromSpell("flametongue weapon", 0x1f58);
			rockbiterWeapon = SpellKeys.CreateFromSpell("rockbiter weapon", 0x1f51);
			windfuryWeapon = SpellKeys.CreateFromSpell("windfury weapon", 0x2028);
			earthlivingWeapon = SpellKeys.CreateFromSpell("earthliving weapon", 0xca12);
			frostbrandWeapon = SpellKeys.CreateFromSpell("frostbrand weapon", 0x1f61);
			heroism = SpellKeys.CreateFromSpell("heroism", 0x7db6);
			bloodlust = SpellKeys.CreateFromSpell("bloodlust", 0xb09);
			waterShield = SpellKeys.CreateFromSpell("watershield", 0xcb9f);
			lightningShield = SpellKeys.CreateFromSpell("lightningshield", 0x144);
			callSpirits = SpellKeys.CreateFromSpell("callofthespirits", 0x1051c);
			callAncestors = SpellKeys.CreateFromSpell("calloftheancestors", 0x1051b);
			callElements = SpellKeys.CreateFromSpell("calloftheelements", 0x1051a);
			totemicRecall = SpellKeys.CreateFromSpell("totemicrecall", 0x9048);
			thunderstorm = SpellKeys.CreateFromSpell("thunderstorm", 0xc922);
			windshear = SpellKeys.CreateFromSpell("windshear", 0xe28a);
			ghostWolf = SpellKeys.CreateFromSpell("ghostwolf", 0xa55);
			bloodFury = SpellKeys.CreateFromSpell("bloodfury", 0x83a6);
			berserking = SpellKeys.CreateFromSpell("berserking", 0x66b9);
			stormStrike = SpellKeys.CreateFromHostileSpell("stormstrike", 0x43d4);
			lavaLash = SpellKeys.CreateFromHostileSpell("lavalash", 0xeac7);
			shamanisticRage = SpellKeys.CreateFromSpell("shamanisticrage", 0x7867);
			feralSpirit = SpellKeys.CreateFromSpell("feral spirit", 0xc94d);
			mount1 = SpellKeys.CreateFromSpell("Mount1", "18989 6777 35713 49379 23249 65641 23248 35712 35714 65637 23247 18991 17453 17465 18992 63641 580 61425 61447 65642 10796 17454 64659 10799 64657 15779 54753 6898 39319 65645 16083 34897 16081 17229 59791 59793 24242 65639 42777 23338 23251 65643 35028 46628 23223 23240 23228 23222 49322 24252 39318 34899 43688 16056 66906 67466 60114 60116 51412 58983 22719 35022 16055 17461 64977 470 60118 60119 48027 22718 59785 59788 22720 22721 22717 22723 22724 64658 35020 10969 17463 64656 50869 43899 34406 458 18990 6899");
			mount2 = SpellKeys.CreateFromSpell("Mount2", "23252 48025 59797 59799 17459 72286 69696 17450 65917 55531 60424 16084 66846 63640 16082 472 35711 35018 23246 66090 41252 34795 10873 17462 22722 16080 30174 17481 39317 34898 63642 42776 10789 23510 63232 66847 8394 10793 66088 66091 68057 23241 43900 23238 23229 23250 65646 23221 23239 65640 23252 35025 23225 68056 23219 65638 23242 23243 23227 33660 32292 35027 35644 17464 6654 6648 39315 34896 68188 68187 39316 34790 63635 63637 6653 8395 63639 63643 17460 23509 63638 61465 61467 61469 61470 35710");
			stoneskinTotem = SpellKeys.CreateFromSpell("stoneskin totem", "8071");
			windfuryTotem = SpellKeys.CreateFromSpell("windfury totem", 0x2140);
			groundingTotem = SpellKeys.CreateFromSpell("grounding totem", 0x1ff1);
			stoneclawTotem = SpellKeys.CreateFromSpell("stoneclaw totem", 0x1662);
			Totem = SpellKeys.CreateFromSpell(TotemName, TotemID);
			feralSpirit.SetDefaultOptions(CastOptions.ChainCast);
			earthShock.SetDefaultOptions(CastOptions.ChainCast);
			flameShock.SetDefaultOptions(CastOptions.ChainCast);
			frostShock.SetDefaultOptions(CastOptions.ChainCast);
			lavaLash.SetDefaultOptions(CastOptions.ChainCast);
			stormStrike.SetDefaultOptions(CastOptions.ChainCast);
			mount1.SetDefaultOptions(CastOptions.ExpectCastBar);
			mount2.SetDefaultOptions(CastOptions.ExpectCastBar);
			lavaBurst.SetDefaultOptions(CastOptions.ExpectCastBar | CastOptions.FastReturn);
			chainLightning.SetDefaultOptions(CastOptions.ExpectCastBar | CastOptions.FastReturn);
			lightningBolt.SetDefaultOptions(CastOptions.ExpectCastBar | CastOptions.ChainCast | CastOptions.FastReturn);
			lesserHealingWave.SetDefaultOptions(CastOptions.ExpectCastBar | CastOptions.FastReturn);
			healingWave.SetDefaultOptions(CastOptions.ExpectCastBar | CastOptions.FastReturn);
			cureToxins.SetDefaultOptions(CastOptions.ChainCast);
		}
		
		#endregion

		#region Combat
		public override void Buff()
		{
			Log.WriteLine("BUFF");
			if (!HasBuff(Me, ghostWolf) && !isMounted)
			{
				if (UseTotems && CanCast(totemicRecall) & TotemsNeedRecall && !Me.Grindables(65.0).Any())
				{
					totemicRecall.Cast();
					TotemsNeedRecall = false;
				}
				if ((DateTime.Now - weaponTimer).TotalMinutes > 29 && CanCast(MainHandEnchant))
				{
					if (Config.MainHandEnchant.Text != "None")
					{
						WaitCooldown(MainHandEnchant);
						MainHandEnchant.Cast();
						if (Config.OffHandEnchant.Text != "None")
						{
							WaitCooldown(OffHandEnchant);
							OffHandEnchant.Cast();
						}
					}
					weaponTimer = DateTime.Now;
				}

				if (Me.Mana < ShieldTwistingMana)
				{
					WaitCooldown(waterShield);
					if (CanCast(waterShield, true))
						waterShield.Cast();
				}
				else if (CanCast(lightningShield, true))
				{
					WaitCooldown(lightningBolt);
					lightningShield.Cast();
				}

				if (CanCast(ghostWolf) && UseGhostWolf && !Me.Grindables(GhostDistance).Any())
				{
					WaitCooldown(ghostWolf);
					ghostWolf.Cast();
				}
				else if (UseMount && !Me.Grindables(MountDistance).Any())
				{
					if (!Me.Lootables(15.0, false).Any())
					{
						WaitCooldown(mount1);
						WaitCooldown(mount2);
						if (CanCast(mount1))
						{
							Input.ReleaseAllKeys();
							mount1.Cast();
							isMounted = true;
						}
						else if (CanCast(mount2))
						{
							Input.ReleaseAllKeys();
							mount2.Cast();
							isMounted = true;
						}
					}
				}
			}
		}

		private void PreCombat(GameUnit target)
		{
			isMounted = false;
			if (!target.IsFacingAway || (target.Distance <= PULL_RANGE - 5))
			{
				if (CanCast(bloodFury))
				{
					bloodFury.Cast();
				}
				if (CanCast(berserking))
				{
					berserking.Cast();
				}
				if (CanCast(bloodlust))
				{
					bloodlust.Cast();
				}
				if (CanCast(shamanisticRage) && (Me.Mana < ShamanisticRageMana))
				{
					shamanisticRage.Cast();
				}
			}
			if (CanCast(feralSpirit))
			{
				feralSpirit.Cast();
			}
			if (UseTotems && (LastTotemLocation.DistanceToSelf > 30f))
			{
				if (UseCallOfAncestors && CanCast(callAncestors))
				{
					if (TotemsNeedRecall && CanCast(totemicRecall))
					{
						totemicRecall.Cast();
					}
					callAncestors.Cast();
					LastTotemLocation = Me.Location;
					TotemsNeedRecall = true;
				}
				else if (UseCallOfSpirits && CanCast(callSpirits))
				{
					if (TotemsNeedRecall && CanCast(totemicRecall))
					{
						totemicRecall.Cast();
					}
					callSpirits.Cast();
					LastTotemLocation = Me.Location;
					TotemsNeedRecall = true;
				}
				else if (UseCallOfElements && CanCast(callElements))
				{
					if (TotemsNeedRecall && CanCast(totemicRecall))
					{
						totemicRecall.Cast();
					}
					callElements.Cast();
					LastTotemLocation = Me.Location;
					TotemsNeedRecall = true;
				}
				else if (CanCast(Totem))
				{
					if (TotemsNeedRecall && CanCast(totemicRecall))
					{
						totemicRecall.Cast();
					}
					Totem.Cast();
					LastTotemLocation = Me.Location;
					TotemsNeedRecall = true;
				}
			}
		}

		protected override CombatResult Combat(GameUnit target)
		{
			try
			{
				Target = target;
				Ticker meleeTicker = new Ticker(500, true);
				DateTime now = DateTime.Now;
				Input.ReleaseAllKeys();
				if (!Target.IsMob)
				{
					return CombatResult.Retry;
				}

				List<GameUnit> attackers = Me.Attackers().OrderBy(e=>e.Distance).ToList();
				List<GameUnit> hostiles = Me.Hostiles(AGGRO_RANGE).Where(mob=>mob.GUID != Target.GUID && mob.Distance < Target.Distance).ToList();
				if (attackers.Any())
					Target = attackers[0];
				else if (hostiles.Any())
					Target = hostiles[0];
				Target.SetAsTarget(false);

				MoveTo(Target, PULL_RANGE);
				Target.Face();

				PreCombat(Target);
				if (!Me.IsInCombat && PullWithLightningBolt)
				{
					WaitCooldown(lightningBolt);
					lightningBolt.Cast();
				}
				while (true)
				{
					int numAttackers = Me.Attackers().Count();
					CombatResult result = CheckCommonCombatResult(Target, numAttackers > 0);
					Thread.Sleep(40);
					//if (((result != CombatResult.Unknown) && !Me.IsCasting) && ((result != CombatResult.Bugged) || (!target.IsInCombat && ((DateTime.Now - now).TotalSeconds > 10.0))))
					if (result != CombatResult.Unknown)
					{
						Log.WriteLine("COMBAT RESULT: " + result.ToString(), new object[0]);
						return result;
					}
					if ((!Me.IsMeleeing && !Me.IsCasting) && meleeTicker.IsReady)
					{
						Input.TapKey('t');
						meleeTicker.Reset();
					}

					Target.Face();

					HandleCombatHealing();
					HandleCombatDistance();
					HandleAdds();
					HandleRunners();
					
					if (Target.Health < MeleeFinisherHealth && numAttackers == 1)
					{
						continue;
					}

					if (CanCast(thunderstorm) && (Target.Distance <= 10.0))
						thunderstorm.Cast();

					if (Target.Distance <= MELEE_RANGE)
					{
						if (CanCast(stormStrike))
							stormStrike.Cast();

						if (CanCast(lavaLash))
							lavaLash.Cast();
					}
					if (Target.Distance <= 27.5)
					{
						if (CanCast(windshear) && Target.IsCasting)
							windshear.Cast();

						if (CanCast(frostShock, Target) && UseFrostShockDebuff && Target.Distance > MELEE_RANGE + 7.0 && !Target.IsCasting)
							frostShock.Cast();

						if ((CanCast(flameShock, Target) && UseFlameShockDebuff) && (Target.Health > 0.5))
							flameShock.Cast();

						if (CanCast(MainShock))
							MainShock.Cast();
					}

					if (CanCast(lavaBurst) && (HasBuff(Target, flameShock) || ((MainShock != flameShock) && !UseFlameShockDebuff)))
						lavaBurst.Cast();

					if (CanCast(lightningBolt))
					{
						if (PullWithLightningBolt && (!Target.IsInCombat || LightningBoltAlways || LightningBoltUntilMeleeRange && Target.Distance >= MELEE_RANGE + 10.0))
							lightningBolt.Cast();
						else if (Target.Hostiles(AGGRO_RANGE).Any() && Me.IsInCombat && Target.Distance > MELEE_RANGE)
							lightningBolt.Cast();
					}
				}
			}
			catch (Exception exception)
			{
				Log.WriteLine("ERROR: " + exception.Message, new object[0]);
				return CombatResult.Retry;
			}
		}
		
		public override bool NeedsRest()
		{
			if (Me.Mana > MIN_MANA)
				if (cureToxins.IsDefined && HasCurableDebuff || healingWave.IsDefined && Me.Health < HealingWaveHealth || lesserHealingWave.IsDefined && Me.Health < LesserHealingWaveHealth)
					return true;
			return base.NeedsRest();
		}

		public override void Rest()
		{
			HandleRestHealing();
			Log.WriteLine("Done rest healing... ready for drinking/eating");
			base.Rest();
		} 
		#endregion

		#region Handlers
		private void HandleAdds()
		{
			List<GameUnit> attackers = Me.Attackers().ToList();
			if (attackers.Count > 1)
			{
				if (StoneclawAdds && CanCast(stoneclawTotem))
				{
					stoneclawTotem.Cast();
				}
				if (Me.Mana < ShieldTwistingMana)
				{
					if (CanCast(waterShield, true))
					{
						waterShield.Cast();
					}
				}
				else if (CanCast(lightningShield, true))
				{
					lightningShield.Cast();
				}
				if (ChainAdds && CanCast(chainLightning))
				{
					if (Target.Health > 0.25)
					{
						Target.BackAway(5.0);
						chainLightning.Cast();
					}
					else
					{
						foreach (GameUnit unit in attackers)
						{
							if (unit.Health > 0.25)
							{
								unit.SetAsTarget(false);
								unit.Face();
								unit.BackAway(5.0);
								chainLightning.Cast();
								break;
							}
						}
					}
				}
			}
		}

		private void HandleCombatDistance()
		{
			if (!Me.IsCasting)
			{
				if (Target.Health < MeleeFinisherHealth && !Target.Hostiles(AGGRO_RANGE).Any())
					MoveTo(Target, MELEE_RANGE);

				if (Me.Mana < MIN_MANA && !Target.Hostiles(AGGRO_RANGE).Any())
				{
					MoveTo(Target, MELEE_RANGE);
					return;
				}

				if (PullWithShock)
				{
					MoveTo(Target, SHOCK_RANGE);
				}
				if (PullWithLightningBolt)
				{
					MoveTo(Target, PULL_RANGE);
				}

				if (Target.IsCasting)
				{
					if (CanCast(windshear) && (Target.Distance <= SHOCK_RANGE))
						windshear.Cast();

					if (LightningBoltAlways && Target.Distance >= MELEE_RANGE + 10.0)
						MoveTo(Target, SHOCK_RANGE);
					else if (!Target.Hostiles(AGGRO_RANGE).Any())
						MoveTo(Target, MELEE_RANGE);
					else
						MoveTo(Target, SHOCK_RANGE);
				}
			}
		}

		private bool HandleCombatHealing()
		{
			int count = Me.Attackers().Count();
			if (count != 0)
			{
				if (CanCast(cureToxins) && HasCurableDebuff)
				{
					CureAllDebuffs();
				}
				if (Me.Target == null)
				{
					return false;
				}
				PolyValues values = HealthTable[(count <= 3) ? (count - 1) : 2];
				double num2 = ((values.CastingHealthOffset + values.a) + (values.b * Math.Pow(Me.Target.Health, 2.0))) + (values.c * Math.Sqrt(Me.Target.Health));
				if (Me.Health < num2)
				{
					if (CanCast(lesserHealingWave))
					{
						lesserHealingWave.Cast(CastOptions.ExpectCastBar);
						lesserHealingWave.Spam = new Ticker(2000);
						return true;
					}
					if ((!lesserHealingWave.IsCastable && !lesserHealingWave.IsDefined) && CanCast(healingWave))
					{
						healingWave.Cast(CastOptions.ExpectCastBar);
						lesserHealingWave.Spam = new Ticker(2000);
						return true;
					}
				}
			}
			return false;
		}

		private void HandleRestHealing()
		{
			if (Me.Mana > MIN_MANA)
			{
				if (CanCast(cureToxins) && HasCurableDebuff)
				{
					CureAllDebuffs();
				}
				WaitCooldown(healingWave);
				WaitCooldown(lesserHealingWave);
				healingWave.Spam.MakeReady();
				lesserHealingWave.Spam.MakeReady();
				if (CanCast(healingWave) && (Me.Health < HealingWaveHealth))
					healingWave.Cast(CastOptions.ExpectCastBar);

				if (CanCast(lesserHealingWave) && (Me.Health < LesserHealingWaveHealth))
					lesserHealingWave.Cast(CastOptions.ExpectCastBar);
			}
		}

		private void HandleRunners()
		{
			if (Target.IsFacingAway && (Target.Health < 0.3))
			{
				var attackers = Me.Attackers().Where(mob=>mob.GUID != Target.GUID).OrderBy(mob=>mob.Distance).ToList();
				if (attackers.Any())
				{
					Target = attackers[0];
					Target.SetAsTarget(false);
					Input.TapKey('t');
					return;
				}

				if (Target.Hostiles(AGGRO_RANGE).Any())
					MoveTo(Target.Location, PULL_RANGE);
				else if (!ChaseRunners)
					MoveTo(Target.Location, SHOCK_RANGE-2);
				else
					MoveTo(Target.Location.ProjectAhead(2), 3);

				if (CanCast(frostShock, Target))
					frostShock.Cast();
				if (CanCast(earthShock))
					earthShock.Cast();
				if (CanCast(flameShock, Target))
					flameShock.Cast();
			}
		}      
		#endregion

		#region Helpers

		public SpellKeyDefinition GetWeaponEnchant(string name)
		{
			switch (name.ToLower())
			{
				case "frostbrand":
					return frostbrandWeapon;

				case "rockbiter":
					return rockbiterWeapon;

				case "windfury":
					return windfuryWeapon;

				case "earthliving":
					return earthlivingWeapon;

				case "flametongue":
					return flametongueWeapon;
			}
			return SpellKeys.CreateFromSpell("dummy", 0x270f);
		}

		public bool CanCast(SpellKeyDefinition spell)
		{
			return (((spell.IsCastable && spell.IsDefined) && spell.Spam.IsReady) && ((InterfaceItem.GetCastingBar().CastPercent > 0.85) || !Me.IsCasting));
		}

		public bool CanCast(SpellKeyDefinition spell, GameUnit target)
		{
			return (CanCast(spell) && ((target != null) ? !HasBuff(target, spell) : true));
		}

		public bool CanCast(SpellKeyDefinition spell, bool isBuff)
		{
			return (CanCast(spell) && (isBuff ? !HasBuff(Me, spell) : true));
		}

		public bool HasBuff(GameUnit player, SpellKeyDefinition spell)
		{
			if (player == null || !spell.IsDefined)
				return false;
			return (HasBuff(player, spell.KeyName) || player.HasBuff(spell.Key.SpellID));
		}

		public bool HasBuff(GameUnit player, string spellName)
		{
			foreach (OmegaBot.Buff buff in player.GetUnitBuffs())
			{
				if (buff.SpellName.ToLower().Contains(spellName.ToLower()))
				{
					return true;
				}
			}
			return false;
		}

		private void MoveTo(GameUnit target, double distance)
		{
			if (!Me.IsCasting && (target.Distance > distance))
			{
				double num;
				if ((distance > PULL_RANGE) && target.IsFacingAway)
					num = distance - 4.0;
				else if ((distance >= SHOCK_RANGE) && target.IsFacingAway)
					num = distance - 2.0;
				else
					num = distance;
				target.GoTo(num, false);
			}
		}


		private void MoveTo(Location location, double distance)
		{
			if (!Me.IsCasting && (location.DistanceToSelf > distance))
			{
				Location loc = new Location(location.X, location.Y, location.Z);
				loc.GoTo(distance, false);
			}
		}

		public void WaitCooldown(SpellKeyDefinition spell)
		{
			while (spell.IsDefined && !Me.IsDead && !CanCast(spell))
				Thread.Sleep(20);
		}

		private void Face(GameUnit target)
		{
			double headingTo = Me.Location.GetHeadingTo(target.Location);
			double heading = Me.Heading;
			headingTo = (headingTo + (6.2831853071795862 - heading)) % 6.2831853071795862;
			double num3 = (headingTo > 3.1415926535897931) ? -(6.2831853071795862 - headingTo) : headingTo;
			Log.WriteLine(((num3 * 180.0) / 3.1415926535897931).ToString(), new object[0]);
		}

		private void CombatFace()
		{
			bool flag = false;
			while (true)
			{
				if (flag)
				{
					Thread.Sleep(5);
				}
				else if (Me.IsInCombat)
				{
					Thread.Sleep(0x30);
				}
				else
				{
					Thread.Sleep(0x3e8);
				}
				if ((Me.Target != null) && Me.IsInCombat)
				{
					int screenX = Input.WoWClientRect.Left + (Input.WoWClientRect.Width / 2);
					int screenY = Input.WoWClientRect.Bottom + (Input.WoWClientRect.Height / 2);
					double num = Me.Target.Heading - Me.Heading;
					if (Math.Abs(num) <= 0.39269908169872414)
					{
						Input.ReleaseMouse(true);
					}
					else if (!((num >= 0.0) || flag))
					{
						flag = true;
						Input.PositionMouse(screenX, screenY);
						Input.PressMouse(true);
						Input.PositionMouse(Input.WoWClientRect.Left, screenY);
					}
					else if (!((num <= 0.0) || flag))
					{
						flag = true;
						Input.PositionMouse(screenX, screenY);
						Input.PressMouse(true);
						Input.PositionMouse(Input.WoWClientRect.Right, screenY);
					}
				}
			}
		}

		private void CureAllDebuffs()
		{
			OmegaBot.Buff[] unitBuffs = Me.GetUnitBuffs();
			bool flag = false;
			bool flag2 = false;
			foreach (OmegaBot.Buff buff in unitBuffs)
			{
				if (buff.IsHarmful)
				{
					if (buff.BuffType == BuffType.Disease)
					{
						flag2 = true;
					}
					if (buff.BuffType == BuffType.Poison)
					{
						flag = true;
					}
				}
			}
			if ((flag && cureToxins.IsDefined) && cureToxins.Spam.IsReady)
			{
				cureToxins.Cast();
			}
			if ((flag2 && cureToxins.IsDefined) && cureToxins.Spam.IsReady)
			{
				cureToxins.Cast();
			}
		}

		public override bool DoBigHeal()
		{
			return true;
		}

		public override bool DoLightHeal()
		{
			return true;
		}

		private void DumpBuffs()
		{
			Log.WriteLine("Dumping buffs: ", new object[0]);
			foreach (OmegaBot.Buff buff in Me.GetUnitBuffs())
			{
				Log.WriteLine(buff.SpellName + " id: " + buff.SpellID, new object[0]);
			}
		}
		
		
		private bool HasCurableDebuff
        {
            get
            {
                foreach (OmegaBot.Buff buff in Me.GetUnitBuffs())
                {
                    if (buff.IsHarmful && (buff.BuffType == BuffType.Poison || buff.BuffType == BuffType.Disease))
                    {
                        return true;
                    }
                }
                return false;
            }
        }


		public class PolyValues
		{
			public double a;
			public double b;
			public double c;
			public double CastingHealthOffset;

			public PolyValues(double _a, double _b, double _c, double _CastingHealthOffset)
			{
				a = _a;
				b = _b;
				c = _c;
				CastingHealthOffset = _CastingHealthOffset;
			}
		} 
		#endregion
    }
}

